package pers.cfeng.server.txManage;

import pers.cfeng.common.DatabaseException;
import pers.cfeng.common.ErrorCode;
import pers.cfeng.server.utils.ByteBufferParser;
import pers.cfeng.server.utils.FaultHandler;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Cfeng
 * 事务管理模块接口方法实现
 * 整个TM的操作其实大多是围绕tid文件的操作
 */

public class TransactionManagerImpl implements TransactionManager {

    //事务管理大多是对tid文件的管理，所以需要nio的RandomAccessFile和相关的channel
    private RandomAccessFile randomAccessFile;
    private FileChannel fileChannel;
    private long tidCounter; //tid计数器
    private Lock counterLock; //使用Lock锁保证线程安全

    TransactionManagerImpl(RandomAccessFile raf, FileChannel fc) {
        this.randomAccessFile = raf;
        this.fileChannel = fc;
        this.counterLock = new ReentrantLock(); //使用可重入锁进行
        checkTidCounter(); //构造Manager需要校验是否tid文件合法性
    }

    /**
     * 初始化时候需要检查文件是否合法，文件头长度是否 == len
     * 校验TID文件是否合法，读取TID_FILE_HEADER中的count, 计算理论长度
     * 如果文件len都小于头的长度，那么必定出错
     */
    private void checkTidCounter() {
        long fileLen = 0;
        try {
            fileLen = randomAccessFile.length(); //文件实际长度
        } catch (IOException e) {
            FaultHandler.forcedStop(new DatabaseException(ErrorCode.BAD_TID_FILE));
        }
        if(fileLen < TransactionConstant.LEN_TID_HEADER_LEN) {
            FaultHandler.forcedStop(new DatabaseException(ErrorCode.BAD_TID_FILE));
        }
        ByteBuffer buffer = ByteBuffer.allocate(TransactionConstant.LEN_TID_HEADER_LEN);
        try {
            fileChannel.position(0);
            fileChannel.read(buffer); //读头部byte[]表示的数据, buffer和channel是对应的
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        //得到TID个数，再计算实际个数
        this.tidCounter = ByteBufferParser.parseLong(buffer.array()); //tid就是头8个字节表示的数据
        long end = getTidPosition(this.tidCounter) + TransactionConstant.TID_FIELD_SIZE; //getTidPosition相当于取得的是tid该状态byte开始的位置，结束位置需要
        if(end != fileLen) {
            FaultHandler.forcedStop(new DatabaseException(ErrorCode.BAD_TID_FILE));
        }
    }

    //依据tid计算该tid在文件中的位置, position是从0开始, 所以tid需要 - 1, buffer就是从position开始，读取buffer.length个byte
    //指向的其实是该tid状态byte开始的地方
    private long getTidPosition(long tid) {
        return  TransactionConstant.LEN_TID_HEADER_LEN + (tid - 1) * TransactionConstant.TID_FIELD_SIZE;
    }

    //改变tid事务状态为status
    private void updateStatus(long tid, byte status) {
        long offset = getTidPosition(tid);
        byte[] tmp = new byte[TransactionConstant.TID_FIELD_SIZE];
        tmp[0] = status; //修改状态为status
        ByteBuffer buffer = ByteBuffer.wrap(tmp);
        try {
            fileChannel.position(offset);
            fileChannel.write(buffer);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        //每次写buffer时候强制刷新一下,避免丢失
        try {
            fileChannel.force(false);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
    }

    //检查对应tid的事务状态是否为status
    private boolean checkTid(long tid, byte status) {
        long offset = getTidPosition(tid);
        ByteBuffer buffer = ByteBuffer.wrap(new byte[TransactionConstant.TID_FIELD_SIZE]);
        try {
            fileChannel.position(offset);
            fileChannel.read(buffer);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        return buffer.array()[0] == status; //读取的byte比较
    }

    //tidCounter加1,同时修改头部表示的数量
    private void incrTidCounter() {
        tidCounter ++;
        //修改数量
        ByteBuffer buffer = ByteBuffer.wrap(ByteBufferParser.long2Byte(tidCounter));
        try {
            fileChannel.position(0);
            fileChannel.write(buffer);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        //每次写buffer时候强制刷新一下,避免丢失
        try {
            fileChannel.force(false);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
    }

    /**
     * 开启一个事务，返回tid, 但是在多线程环境下，并发修改tidCounter，加上Lock
     * 1. 头部的数量修改, 2. tid对应位置的status写入
     */
    @Override
    public long begin() {
        this.counterLock.lock();
        try {
            long tid = tidCounter + 1; //当前事务
            updateStatus(tid, TransactionConstant.FIELD_TRAN_RUNNING);  //事务激活
            incrTidCounter(); //头部size增加
            return tid;
        } finally {
            this.counterLock.unlock();
        }
    }

    //提交事务，就是将tid状态改为committed即可
    @Override
    public void commit(long tid) {
        updateStatus(tid, TransactionConstant.FIELD_TRAN_COMMITTED);
    }

    @Override
    public void rollback(long tid) {
        updateStatus(tid, TransactionConstant.FIELD_TRAN_ROLLBACK);
    }

    //tid为0为non transaction, 状态为已提交
    @Override
    public boolean isRunning(long tid) {
        if(tid == TransactionConstant.NONE_TID) return false;
        return checkTid(tid, TransactionConstant.FIELD_TRAN_RUNNING);
    }

    @Override
    public boolean isCommitted(long tid) {
        if(tid == TransactionConstant.NONE_TID) return true;
        return checkTid(tid, TransactionConstant.FIELD_TRAN_COMMITTED);
    }

    @Override
    public boolean isRollback(long tid) {
        if(tid == TransactionConstant.NONE_TID) return false;
        return checkTid(tid, TransactionConstant.FIELD_TRAN_ROLLBACK);
    }

    //关闭事务文件, randomAccessFile和Channel不使用时就关闭
    @Override
    public void close() {
        try {
            fileChannel.close();
            randomAccessFile.close();
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
    }
}
